iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Modern Web

了不起的 Svelte系列 第 25

第 25 天:Svelte 的互動百寶箱:Store(二)

  • 分享至 

  • xImage
  •  

第 25 天:Svelte 的互動百寶箱:Store(二)

第 25 天要講的事

  1. 可複寫的 Store:writable Store(續)
  2. Store 的自動訂閱

  經過昨天的介紹,我們大概對 Svelte 所提供的 Store 物件有了一些基礎的認識,原本抽象的 Store 其面貌似乎也漸漸明朗起來。那就讓我們順著昨天文章的脈絡繼續討論 Store 的相關應用吧。

可複寫的 Store:writable Store(續)

  昨天介紹了 writable Store 的 subscribeset 這兩種方法。其中 subscribe 這個方法會註冊一個回呼函式(callback function),每當 Store 當中的變數發生改變,就執行這個回呼函式,利用這個特性,我們可以將 Svelte 元件內的變數跟一般 Javscript檔案內的變數做到資料連動的表現。另一個方法 set 則比較簡單,藉由這個方法我們可以直接將 Store 當中的變數更新成指定的值。
  今天則是要來介紹第三個方法 update。顧名思義,updateset 一樣,想必都可以用來更新 Store 當中的變數。跟 set 直接將變數更新成指定的值不同,update 是從現有的值出發,更新成一個相對應的值。
舉例來說,如果我們需要將 Store 當中的變數更新 0,也就是特定的一個值,這個動作可以藉由 set 完成。然而,如果我們想要將 Store 當中的變數增加一,更新成比現在的值還要多一的數字,也就是 n + 1,這個時候就需要用 update 了。
  讓我們實際在 Svelte 的 REPL (Read-Eval-Print-Loop) 介面練習一下,看一看程式碼該怎麼寫:

/src/store.js
import { writable } from 'svelte/store';

export const stateStore = writable(0);
  • 第三行:export const stateStore = writable(0);
      用 0 初始化一個 writable 的 Store。

  先在 Javascript 檔案當中做好 writable Store 之後,接著來到 Svelte 元件來使用這個 Store 物件。

/src/App.svelte
<script>
  import { onMount } from 'svelte';
  import { stateStore } from './store.js';
	
  let value;
	
  stateStore.subscribe((state) => value = state);
	
  onMount(() => {
    const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
    return () => clearInterval(interval)
  })
</script>

<h1>Now is {value}!</h1>
  • 第三行:import { stateStore } from './store.js';
      引入我們的 Store 物件 stateStore

  • 第五行:let value;
      在 Svelte 元件當中宣告一個變數 value

  • 第七行:stateStore.subscribe((state) => value = state);
      用 subscribe 的方法將 Store 當中的變數跟 Svelte 元件中的變數作連動。

  • 第十行:const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
      利用 update 來更新 Store 當中的變數。update 需要一個用來表明該如何更新變數的函式作為參數。以這個例子來說,就是 (state) => state + 1。這個函式的參數表示現在 Store 當中的變數,並且回傳一個值,做為更新過後的變數。也就是說,透過 setInterval(() => stateStore.update((state) => state + 1), 1000),我們就會把 stateStore 這個 Store 當中的變數一秒一秒的往上加。

https://i.ibb.co/16h518G/25.gif
圖一、一秒一秒往上加

Store 的自動訂閱 (auto-subscription)

  我們上面練習的程式碼看起來挺成功的,透過 Store,成功讓 Javascript 的變數跟 Svelte 元件當中的變數連接在一起。唯一有一個小小的問題,那就是我們利用 subscribe 方法讓兩個變數連接在一起,卻沒有宣告解除連接的方法。當 Svelte 元件從 DOM 當中卸載之後,Svelte 元件中的變數就是一個沒有效果不被需要的變數了,可是 Javascript 當中的變數卻還是依然試圖跟已經卸載的 Svelte 元件中的變數做連動,這麼一來就會浪費記憶體資源。
為了彌補這個錯誤,讓我們修改一下 Svelte 元件 App.svelte 當中的程式碼:

/src/App.svelte
<script>
  import { onMount, onDestroy } from 'svelte';
  import { stateStore } from './store.js';
	
  let value;
	
  const unsubscribe = stateStore.subscribe((state) => value = state);
	
  onMount(() => {
    const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
    return () => clearInterval(interval)
  })

  onDestroy(unsubscribe);
</script>

<h1>Now is {value}!</h1>
  • 第二行:import { onMount, onDestroy } from 'svelte';
      除了 onMount 之外,也讓我們引入 onDestroy。顧名思義,這也是一個 Svelte 提供的生命週期函式,會在 Svelte 元件被卸載時執行。

  • 第七行:const unsubscribe = stateStore.subscribe((state) => value = state);
      在執行 subscribe 這個方法的同時,其實會得到一個回傳函式。如果呼叫這個回傳函式,就會終止 subscribe 的行為,而我們把這個可以終止 subscribe 行為的函式放在 unsubscribe 裡面。按照這一段程式碼具體解釋一次,當我們呼叫 subscribe 的時候,我們就向 stateStore 註冊了一個回呼函式 (callback function),也就是 (state) => value = state。每當 stateStore 當中的變數發生改變,就會執行這個回呼函式。同時,當我們呼叫 subscribe 的時候,也得到了一個函式,也就是 unsubscribe。當我們執行這個 unsubscribe 函式,那麼先前利用 subscribestateStore 註冊的回呼函式 (callback function) 就會作廢。

  • 第十四行:onDestroy(unsubscribe);
      當 Svelte 元件從 DOM 卸載時,執行 unsubscribe 函式。這麼一來,我們的 Store 就不會一直想要試著連動已經卸載的 Svelte 元件當中的變數了。

https://i.ibb.co/tsz5Dpp/25.gif
圖二、記得做好善後工作

  這麼一來就完美的將 Javascript 的變數跟 Svelte 元件中的變數連動在一起了。不過這樣子寫似乎有些麻煩。首先要先使用 subscribe 做出連動,接著還不能忘記元件卸載時要執行 unsubscribe 避免浪費記憶體空間資源。當我們需要連動的變數越來越多,這樣的程式碼寫起來似乎會越來越頭痛。
  因此 Svelte 很貼心的提供了一個簡單的方法來做到這個效果,也就是自動訂閱 (auto-subscription)。來看看怎麼做吧:

/src/App.svelte
<script>
  import { onMount } from 'svelte';
  import { stateStore } from './store.js';
	
  $: value = $stateStore;
	
  onMount(() => {
    const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
    return () => clearInterval(interval)
  })
</script>

<h1>Now is {value}!</h1>
  • 第二行:import { onMount } from 'svelte';
      使用自動訂閱的方式,會直接幫我們連接/解除連接,所以不需要再引入 onDestroy 去處理卸載時的例行公事了。

  • 第五行:$: value = $stateStore;
      前面提過,stateStore 其實是 Svelte 提供的 Store 物件,當中儲存著一個變數。這個變數可以透過 setupdate 等方法來做改變,同時也可以透過 subscribe 這個方法連動到 Svelte 元件的變數,藉此來取得 Store 物件當中的變數的值。繞了很大一圈不是嗎?還因此需要準備 unsubscribe 這個方法來避免記憶體資源浪費。所以 Svelte 更進一步提供了自動訂閱的功能來簡化這個流程。自動訂閱,另一種說法就是可以讓我們直接從 Store 物件當中取得變數的值。怎麼做呢?stateStore 是 Store 物件,$stateStore 就是 Store 物件當中的變數的值。並且用 $: value 來做出互動宣告(詳見第 08 天:Svelte 中的 Javascript:宣告)的效果。只要 Store 物件當中的變數一改變,value 就會因為重新賦值而一起更新。

  既然 $stateStore 可以直接取得 stateStore 這個 Store 物件當中的變數的值,那麼這一段程式碼似乎還有簡化的空間:

/src/App.svelte
<script>
  import { onMount } from 'svelte';
  import { stateStore } from './store.js';
	
  // $: value = $stateStore;
	
  onMount(() => {
    const interval = setInterval(() => stateStore.update((state) => state + 1), 1000);
    return () => clearInterval(interval)
  })
</script>

<h1>Now is {$stateStore}!</h1>
  • 第五行:// $: value = $stateStore;
      既然我們可以透過 $stateStore 直接取得 Store 物件當中變數的值,那是不是可以直接拿來用,也不需要這一行了。

  • 第十三行:<h1>Now is {$stateStore}!</h1>
      直接在 HTML 元素當中展開 Javascript 的領域,並且使用 $stateStore 直接取得 Store 物件當中變數的值。

https://i.ibb.co/znXsN4K/25.gif
圖三、利用自動訂閱,簡化再簡化

  那麼今天關於 Store 的討論就到這邊了,謝謝大家。


上一篇
第 24 天:Svelte 的互動百寶箱:Store(一)
下一篇
第 26 天:Svelte 的互動百寶箱:Store(三)
系列文
了不起的 Svelte30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言